<?php

/**
 * Convenience methods for creating form elements.
 *
 * @author     Mon Zafra <monzee@gmail.com>
 * @copyright  (c)2009 Mon Zafra
 * @category   Mz
 * @package    Mz_Helper
 * @subpackage Action
 * @license    http://mz-project.googlecode.com/svn/trunk/LICENSE    MIT License
 * @version    SVN: $Id: FormBuilder.php 3 2009-02-18 18:55:27Z monzee $
 * @todo Methods that add a single decorator, validator, filter or option
 */
class Mz_Helper_Action_FormBuilder extends Zend_Controller_Action_Helper_Abstract
{
    /**
     * @var Zend_Form
     */
    protected $_form;
    protected $_groupElems = array();
    protected $_curGroup;
    protected $_curGroupOpts = array();
    protected $_curDecorators = array();
    protected $_curFilters = array();
    protected $_curValidators = array();
    protected $_curAddlOpts = array();

    /**
     * Constructor.
     *
     * @param Zend_Form $form
     */
    public function __construct(Zend_Form $form = null)
    {
        $this->_form = $form;
    }

    /**
     * Auto invoke method. Start chaining.
     *
     * @param Zend_Form $form
     * @return Mz_Helper_Action_FormBuilder
     */
    public function direct(Zend_Form $form = null)
    {
        if (null !== $form) {
            $this->_form = $form;
        }
        return $this;
    }

    /**
     *
     * @param Zend_Form $form
     * @return Mz_Helper_Action_FormBuilder
     */
    public function setForm(Zend_Form $form)
    {
        $this->_form = $form;
        return $this;
    }

    /**
     * Lazy load form
     *
     * @return Zend_Form
     */
    public function getForm()
    {
        if (!$this->_form instanceof Zend_Form) {
            $this->_form = new Zend_Form;
        }
        return $this->_form;
    }

    /**
     * Sets the form decorators.
     *
     * @param array|string $decorators
     * @return Mz_Helper_Action_FormBuilder
     */
    public function formDecorators($decorators)
    {
        if (!is_array($decorators)) {
            $decorators = func_get_args();
        }
        $this->getForm()->setDecorators($decorators);
        return $this;
    }

    /**
     * Returns the current form and reset.
     *
     * @return Zend_Form
     */
    public function end()
    {
        if ($this->_curGroup != '') {
            $this->endGroup();
        }
        $form = $this->getForm();
        $this->_form = null;
        return $form;
    }

    /**
     * All elements added after calling this and before calling endGroup()
     * will be grouped.
     *
     * @param string $group Group name
     * @param array  $opts  Display group options
     * @return Mz_Helper_Action_FormBuilder
     */
    public function group($group, $opts=array())
    {
        if ($this->_curGroup != '') {
            $this->endGroup();
        }
        $this->_curGroup = $group;
        $this->_curGroupOpts = $opts;
        return $this;
    }

    /**
     * Build group.
     *
     * @return Mz_Helper_Action_FormBuilder
     */
    public function endGroup()
    {
        $options = $this->_curGroupOpts;

        $this->getForm()
             ->addDisplayGroup($this->_groupElems, $this->_curGroup, $options);

        $this->_curGroup = '';
        $this->_groupElems = array();

        return $this;
    }

    /**
     * Sets the group decorators.
     *
     * @param array|string $decorators
     * @return Mz_Helper_Action_FormBuilder
     */
    public function groupDecorators($decorators)
    {
        if (!is_array($decorators)) {
            $decorators = func_get_args();
        }
        $this->_curGroupOpts['decorators'] = $decorators;
        return $this;
    }

    /**
     *
     * @return Mz_Helper_Action_FormBuilder
     */
    public function endGroupDecorators()
    {
        if (isset($this->_curGroupOpts['decorators'])) {
            unset($this->_curGroupOpts['decorators']);
        }
        return $this;
    }

    /**
     * These decorators will be used for the added elements until
     * endDecorators() is called.
     *
     * @param mixed $decorators
     * @return Mz_Helper_Action_FormBuilder
     */
    public function decorators($decorators = array())
    {
        if (!is_array($decorators) || func_num_args() > 1) {
            $decorators = func_get_args();
        }
        $this->_curDecorators = $decorators;
        return $this;
    }

    /**
     *
     * @return Mz_Helper_Action_FormBuilder
     */
    public function endDecorators()
    {
        $this->_curDecorators = array();
        return $this;
    }

    /**
     * Same deal as decorators(), for validators.
     *
     * @param array $validators
     * @return Mz_Helper_Action_FormBuilder
     */
    public function validators($validators = array())
    {
        if (!is_array($validators) || func_num_args() > 1) {
            $validators = func_get_args();
        }
        $this->_curValidators = $validators;
        return $this;
    }

    /**
     *
     * @return Mz_Helper_Action_FormBuilder
     */
    public function endValidators()
    {
        $this->_curValidators = array();
        return $this;
    }

    /**
     * Again, filters this time.
     *
     * @param array $filters
     * @return Mz_Helper_Action_FormBuilder
     */
    public function filters($filters = array())
    {
        if (!is_array($filters) || func_num_args() > 1) {
            $filters = func_get_args();
        }
        $this->_curFilters = $filters;
        return $this;
    }

    /**
     *
     * @return Mz_Helper_Action_FormBuilder
     */
    public function endFilters()
    {
        $this->_curFilters = array();
        return $this;
    }

    /**
     * Additional element options.
     *
     * @param array $opts
     * @return Mz_Helper_Action_FormBuilder
     */
    public function options($opts = array())
    {
        $this->_curAddlOpts = $opts;
        return $this;
    }

    /**
     *
     * @return Mz_Helper_Action_FormBuilder
     */
    public function endOptions()
    {
        $this->_curAddlOpts = array();
        return $this;
    }

    /**
     *
     * @return array
     */
    public function getCurrentOptions()
    {
        $ret = array();
        if (!empty($this->_curDecorators)) {
            $ret['decorators'] = $this->_curDecorators;
        }
        if (!empty($this->_curFilters)) {
            $ret['filters'] = $this->_curFilters;
        }
        if (!empty($this->_curValidators)) {
            $ret['validators'] = $this->_curValidators;
        }
        return $ret + $this->_curAddlOpts;
    }

    /**
     * Adds an element.
     *
     * @param string $type
     * @param string $name
     * @param array   $options
     * @return Mz_Helper_Action_FormBuilder
     */
    public function addElement($type, $name, $options = array())
    {
        $options += $this->getCurrentOptions();
        $this->getForm()->addElement($type, $name, $options);
        if ('' != $this->_curGroup) {
            $this->_groupElems[] = $name;
        }
        return $this;
    }

    /**
     * Magic caller.
     *
     * @param string $method
     * @param array  $args
     * @return mixed
     */
    public function __call($method, $args)
    {
        // proxy to form if it's a valid Zend_Form method
        $form = $this->getForm();
        if (method_exists($form, $method)) {
            call_user_func_array(array($form, $method), $args);
            return $this;
        }
        // proxy add* calls to addElement
        if (strtolower(substr($method, 0, 3)) == 'add') {
            $type = substr($method, 3);
            array_unshift($args, $type);
            return call_user_func_array(array($this, 'addElement'), $args);
        }
        throw new Zend_Form_Exception("Bad method call ($method)");
    }

}
